package com.wjh.www.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wjh.www.annotation.springmvc.RequestBody;
import com.wjh.www.annotation.springmvc.RequestMapping;
import com.wjh.www.annotation.springmvc.RequestParam;
import com.wjh.www.config.IocContext;
import com.wjh.www.entity.dto.CommonResult;
import com.wjh.www.entity.dto.ResultUtil;
import com.wjh.www.entity.dto.TypeEnum;
import com.wjh.www.entity.vo.ExcelVo;
import com.wjh.www.util.BeanUtil;
import com.wjh.www.util.ControllerUtil;
import com.wjh.www.util.GetRequestJsonUtil;
import com.wjh.www.util.LoggerUtil;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.logging.Logger;

/**
 * @author wjh
 * @date 2021/9/9 15:50
 * @Package com.wjh.anyview.controller
 */
public class DispatcherController extends HttpServlet {
    private final Logger logger = LoggerUtil.getLogger();

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String url = req.getRequestURI();
        //截取最后一个反斜杠后面的字符串，获取要访问的接口，通过反射转发req，resp到对应的接口
        String methodName = url.substring(url.lastIndexOf("/"));
        String beanName = this.getClass().getSimpleName();
        Object object = IocContext.getBean(beanName.substring(0, 1).toLowerCase() + beanName.substring(1));
        //获取被public修饰的所有方法
        Method[] methods = object.getClass().getDeclaredMethods();
        //存放反射需要的参数的集合
        List<Object> list = new ArrayList<>();
        Method method = null;
        JSONObject json = GetRequestJsonUtil.getRequestJsonObject(req);
        logger.info("请求参数转化的json对象：" + json);
        for (Method m : methods) {
            //遍历该类的方法找到对应的注解
            if (m.getAnnotation(RequestMapping.class).value().equals(methodName)) {
                method = m;
                try {
                    //获取匹配成功的方法的所有参数
                    Parameter[] parameters = m.getParameters();
                    logger.info("被访问接口的方法参数：" + Arrays.toString(parameters));
                    for (Parameter parameter : parameters) {
                        //如果标注了@RequestBody注解
                        if (parameter.isAnnotationPresent(RequestBody.class)) {
                            Map<String, String[]> map = req.getParameterMap();
                            //如果是json传输则需要把value转String，并封装成Map<String,String[]>
                            if (map.size() == 0) {

                                Map<String, Object> map1 = JSON.parseObject(String.valueOf(json));
                                Map<String, String[]> map2 = new HashMap<>(10);
                                for (String s : map1.keySet()) {
                                    map2.put(s, new String[]{map1.get(s).toString()});
                                }

                                map = map2;
                            }

                            Object o = BeanUtil.packParams(parameter.getType(), map);
                            list.add(o);
                        }

                        //如果参数标注了@RequestParam注解
                        if (parameter.isAnnotationPresent(RequestParam.class)) {
                            Object param = req.getParameter(parameter.getAnnotation(RequestParam.class).value());
                            //如果是json传输则直接取值即可
                            if (param == null) {
                                param = json.get(parameter.getAnnotation(RequestParam.class).value());
                            }

                            //没有@RequestBody就直接从req中获取参数，并进行类型的转化（因为req.getParameter获取的都是String）
                            Object o = parameter.getType().getConstructor(String.class).newInstance(param.toString());
                            list.add(o);
                        }

                    }
                } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | InstantiationException e) {
                    e.printStackTrace();
                }
                break;
            }
        }
        //调用接口的返回值，并返回给前端
        CommonResult<?> result = null;
        try {
            assert method != null;
            logger.info("传入的参数：" + Arrays.toString(list.toArray()));
            result = (CommonResult<?>) method.invoke(object, list.toArray());
            logger.info("返回给前端的结果集：" + result.toString());
        } catch (IllegalAccessException | InvocationTargetException e) {
            logger.severe("调用接口发送反射异常，method：" + method + "，参数：" + list);
            e.printStackTrace();
        }

        assert result != null;
        TypeEnum typeEnum = result.typeEnumEnum;
        switch (typeEnum) {
            case JSON: {
                try {
                    //返回JSON给前端
                    ControllerUtil.toJson(resp, result);
                } catch (IOException e) {
                    logger.severe("返回JSON格式错误：" + result);
                    ResultUtil.printCode(resp, CommonResult.operateFailWithMessage("内部服务器错误"), 500);
                }
            }
            case FILE: {
                try {
                    //返回excel表格给前端
                    ExcelVo excelVo = (ExcelVo) result.getData();
                    ControllerUtil.pushExcel(resp, excelVo.getFileName(), excelVo.getWb());
                } catch (IOException ioException) {
                    logger.severe("返回FILE格式错误：" + result.getData());
                    ioException.printStackTrace();
                    ResultUtil.printCode(resp, CommonResult.operateFailWithMessage("内部服务器错误"), 500);
                }
            }
        }

    }

}
